home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
BCI NET
/
BCI NET Dec 94.iso
/
archives
/
programming
/
source
/
fbm12s.lha
/
fbham.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-07-18
|
15KB
|
591 lines
/*****************************************************************
* fbham.c: FBM Release 1.0 25-Feb-90 Michael Mauldin
*
* Copyright (C) 1989,1990 by C. Harald Koch & Michael Mauldin.
* Permission is granted to use this file in whole or in part for
* any purpose, educational, recreational or commercial, provided
* that this copyright notice is retained unchanged. This software
* is available to all free of charge by anonymous FTP and in the
* UUNET archives.
*
* fbham.c: Write a 24bit RGB file as an Amiga HAM IFF file
*
* USAGE
* % fbham < image1 > image2
*
* EDITLOG
* LastEditDate = Mon Jun 25 00:56:54 1990 - Michael Mauldin
* LastFileName = /usr2/mlm/src/misc/fbm/fbham.c
*
* HISTORY
* 13-Jul-89 Michael Mauldin (mlm) at Carnegie Mellon University
* Beta release (version 0.95) mlm@cs.cmu.edu
*
* 20-Apr-89 C. Harald Koch (chk) at DCIEM Toronto.
* Created. chk@ben.dciem.dnd.ca
*
*================================================================
* based on ray2.c from DBW_Render, Copyright 1987 David B. Wecker
*
* From: chk@dretor.dciem.dnd.ca (C. Harald Koch)
* Subject: fbham.c - convert a 24bit FBM file to an IFF file using HAM mode
* To: Michael.Mauldin@nl.cs.cmu.edu (Michael Maudlin)
* Date: Mon, 1 May 89 17:21:16 EDT
* X-Mailer: ELM [version 2.2 PL0]
*
* This is the source to my program to convert from a 24bit FBM file to Amiga
* HAM mode. It is based on Dave Wecker's RAY2 program, which converts the
* output of his raytracer to HAM mode. His code uses the Amiga graphics
* library to plot the pixels in a framebuffer for encoding; this version will
* run standalone.
*
* There may be bugs, although it works well for me here. It is probably not in
* the format you want for FBM source; Please go ahead and modify it. There is
* probably room for some command line options for various things (such as the
* verbose output, run length encoding, etc) which I haven't needed and so
* haven't added.
*
* I will send you the IRIS programs when I get them. Enjoy!
* -chk
*****************************************************************/
#include <stdio.h>
#include <math.h>
#include "fbm.h"
# define USAGE "Usage: fbham < image > image"
#ifndef lint
static char *fbmid =
"$FBM fbham.c <1.0> 25-Jun-90 (C) 1989 by C. Harald Koch, source \
code available free from MLM@CS.CMU.EDU and from UUNET archives$";
#endif
#define DEPTH 6 /* max depth of Amiga HAM image */
#define BMHDsize 20L /* chunk sizes */
#define CMAPsize 96L
#define CAMGsize 4L
#define BODYsize ((long)(16000L))
#define FORMsize (BODYsize+CAMGsize+CMAPsize+BMHDsize+36L)
unsigned char *planes[DEPTH]; /* bitplane pointers */
static int comp = 1; /* compress image? */
static int depth = DEPTH; /* depth of image being created */
static int colors = 16; /* number of colors in color lookup table */
static int colorstats[4096][2]; /* color usage statistics */
static int row_size; /* number of bytes in IFF row of data */
/* temp variables for writing IFF file */
char str[40];
long lng, pos1, pos2;
short wrd;
unsigned char byt;
unsigned char *dest, destbuf[BUFSIZ];
main(argc, argv)
char *argv[];
{
FBM input;
/* Clear the memory pointers so alloc_fbm won't be confused */
input.cm = input.bm = (unsigned char *) NULL;
/* Read the image and rotate it */
if (!read_bitmap(&input, argc > 0 ? argv[1] : (char *) NULL))
{
exit(1);
}
/* slight sanity checks. could be better. */
if (input.hdr.physbits != 8 || input.hdr.planes != 3) {
fprintf(stderr, "input file must be 24 bit RGB data\n");
exit(1);
}
/* convert to HAM */
if (!fbm2ham(&input, stdout)) {
exit(1);
}
exit(0);
}
/************************ run length encoding from Amiga RKM *****************/
#define DUMP 0 /* list of different bytes */
#define RUN 1 /* single run of bytes */
#define MinRun 3 /* shortest allowed run */
#define MaxRun 128 /* longest run (length is signed char) */
#define MaxDat 128 /* longest block of unencoded data */
#define GetByte() (*source++)
#define PutByte(c) { *dest++ = (c); ++PutSize; }
#define OutDump(nn) dest = PutDump(dest,nn);
#define OutRun(nn,cc) dest = PutRun(dest,nn,cc);
int PutSize;
char buf[256];
unsigned char *
PutDump(dest, nn)
unsigned char *dest;
int nn;
{
int i;
PutByte(nn - 1);
for (i = 0; i < nn; i++)
PutByte(buf[i]);
return (dest);
}
unsigned char *
PutRun(dest, nn, cc)
unsigned char *dest;
int nn, cc;
{
PutByte(-(nn - 1));
PutByte(cc);
return (dest);
}
/* PackRow - pack a row of data using Amiga IFF RLE */
int
PackRow(pSource, pDest, RowSize)
unsigned char **pSource, **pDest;
int RowSize;
{
unsigned char *source, *dest;
char c, lastc = '\000';
int mode = DUMP, nbuf = 0, /* number of chars in buf */
rstart = 0; /* buf index current run starts */
source = *pSource;
dest = *pDest;
PutSize = 0;
buf[0] = lastc = c = GetByte();
nbuf = 1;
RowSize--;
for (; RowSize; --RowSize) {
buf[nbuf++] = c = GetByte();
switch (mode) {
case DUMP:
if (nbuf > MaxDat) {
OutDump(nbuf - 1);
buf[0] = c;
nbuf = 1;
rstart = 0;
break;
}
if (c == lastc) {
if (nbuf - rstart >= MinRun) {
if (rstart > 0)
OutDump(rstart);
mode = RUN;
}
else if (rstart == 0)
mode = RUN;
}
else
rstart = nbuf - 1;
break;
case RUN:
if ((c != lastc) || (nbuf - rstart > MaxRun)) {
OutRun((nbuf - 1) - rstart, lastc);
buf[0] = c;
nbuf = 1;
rstart = 0;
mode = DUMP;
}
break;
}
lastc = c;
}
switch (mode) {
case DUMP:
OutDump(nbuf);
break;
case RUN:
OutRun(nbuf - rstart, lastc);
break;
}
*pSource = source;
*pDest = dest;
return (PutSize);
}
/******************* end of RKM RL encoding routines **********************/
/* build_histogram - count frequency of 12bit colors in an FBM image */
build_histogram(image)
FBM *image;
{
int i, j, t0, t1, gap, val, used;
unsigned char *rp, *gp, *bp;
int diff;
/* initialize color statistics. */
for (i = 0; i < 4096; i++) {
colorstats[i][0] = i;
colorstats[i][1] = 0;
}
/* obtain pointers to the beginning of each plane (Red, Green, Blue) */
rp = image->bm;
gp = rp + image->hdr.plnlen;
bp = gp + image->hdr.plnlen;
/* count the number of occurences of each color in the image */
for (i = 0 ; i < image->hdr.plnlen ; i++) {
val = ((*rp++ & 0xf0) << 4) + (*gp++ & 0xf0) + ((*bp++ & 0xf0) >> 4);
val %= 4096;
if (colorstats[val][1] < 32767) {
colorstats[val][1]++;
}
}
/* sort the color stats in order of decreasing usage */
for (gap = 2048; gap > 0; gap /= 2) {
for (i = gap; i < 4096; i++) {
for (j = i - gap; j >= 0 &&
colorstats[j][1] < colorstats[j + gap][1]; j -= gap) {
t0 = colorstats[j][0];
t1 = colorstats[j][1];
colorstats[j][0] = colorstats[j + gap][0];
colorstats[j][1] = colorstats[j + gap][1];
colorstats[j + gap][0] = t0;
colorstats[j + gap][1] = t1;
}
}
}
/* count the number of colors actually used in the image */
for (used = 0; used < 4096 && colorstats[used][1] > 0; used++);
fprintf(stderr, "Used %d colors out of a possible 4096\n", used);
}
/*-
* plot_pixel - plot a color in an Amiga style bitmap (one plane per bit)
*
* Description:
* A somewhat optimized routine to set/reset one bit in each plane
* at the specified (x,y) coordinates. plane i gets the bit in
* position i of color. a replacement for the Amiga WritePixel call.
-*/
plot_pixel(planes, x, y, color)
unsigned char *planes[];
int x, y;
register int color;
{
register int bit;
register unsigned char shifted_bit = 1 << (7-(x%8));
register int array_offset = y * row_size + x/8;
register int i;
for (i = 0; color && i < depth; i++) {
bit = color & 1;
color >>= 1;
if (bit) *(planes[i] + array_offset) |= shifted_bit;
}
}
/*-
* fbm2ham - write an FBM image in HAM IFF format on the given file pointer.
*
-*/
fbm2ham(image, ofil)
FBM *image;
FILE *ofil;
{
int i, j, k, gap, t0, t1, prgb, crgb, cpix, ppix, maxdis, used;
int c1, c2, diff;
unsigned char *rp, *gp, *bp;
fprintf(stderr, "Building a color histogram:\n");
build_histogram(image);
/* convert the image to an Amiga HAM bitplane based image */
cpix = 0;
crgb = colorstats[0][1];
rp = image->bm;
gp = rp + image->hdr.plnlen;
bp = gp + image->hdr.plnlen;
row_size = ((image->hdr.cols + 15) / 16) * 2;
diff = image->hdr.rowlen - image->hdr.cols;
for (i = 0; i < depth ; i++) {
planes[i] = (unsigned char *)malloc(row_size * image->hdr.rows);
}
for (i = 0; i < image->hdr.rows; i ++) {
/* verbose output because this program is slow even on a Sun */
if (i%50 == 0) { fprintf(stderr, "...%d", i); fflush(stderr); }
/* step through current row, converting FBM pixel to nearest equivalent
* HAM pixel */
for (j = 0; j < image->hdr.cols; j++) {
prgb = crgb;
crgb = ((*rp++ & 0xf0) << 4) + (*gp++ & 0xf0) + ((*bp++ & 0xf0) >> 4);
crgb %= 4096;
ppix = cpix;
/* start of scan line is ALWAYS an absolute color */
if (j == 0)
cpix = getcolor(ppix, &crgb, -1);
else
cpix = getcolor(ppix, &crgb, prgb);
/* plot the computed pixel */
plot_pixel(planes, j, i, cpix);
}
rp += diff;
gp += diff;
bp += diff;
}
fprintf(stderr, "\n");
/* Now we write the planes[] array we just created in ILBM format */
sprintf(str, "FORM");
fwrite(str, 1, 4, ofil);
pos1 = ftell(ofil);
lng = FORMsize;
fwrite(&lng, 4, 1, ofil);
sprintf(str, "ILBM");
fwrite(str, 1, 4, ofil);
sprintf(str, "BMHD");
fwrite(str, 1, 4, ofil);
lng = BMHDsize;
fwrite(&lng, 4, 1, ofil);
wrd = image->hdr.cols;
fwrite(&wrd, 2, 1, ofil); /* width */
wrd = image->hdr.rows;
fwrite(&wrd, 2, 1, ofil); /* height */
wrd = 0;
fwrite(&wrd, 2, 1, ofil); /* top */
wrd = 0;
fwrite(&wrd, 2, 1, ofil); /* left */
byt = depth;
fwrite(&byt, 1, 1, ofil); /* Depth */
byt = 0;
fwrite(&byt, 1, 1, ofil); /* mask */
byt = comp;
fwrite(&byt, 1, 1, ofil); /* compress */
byt = 0;
fwrite(&byt, 1, 1, ofil); /* pad */
wrd = 0;
fwrite(&wrd, 2, 1, ofil); /* transparency */
byt = 10;
fwrite(&byt, 1, 1, ofil); /* aspect x */
byt = 11;
fwrite(&byt, 1, 1, ofil); /* aspect y */
wrd = image->hdr.cols;
fwrite(&wrd, 2, 1, ofil); /* page width */
wrd = image->hdr.rows;
fwrite(&wrd, 2, 1, ofil); /* page height */
/* CAMG chunk for displaying files on the Amiga. should include at least
* the HAM flag; I also add lace if the picture is large enough. Perhaps
* this should be an option. -CHK */
sprintf(str, "CAMG");
fwrite(str, 1, 4, ofil);
lng = CAMGsize;
fwrite(&lng, 4, 1, ofil);
if (image->hdr.rows > 200)
lng = 0x804L; /* HAM | LACE */
else
lng = 0x800L; /* HAM */
fwrite(&lng, 4, 1, ofil);
/* write out the color lookup table */
sprintf(str, "CMAP");
fwrite(str, 1, 4, ofil);
lng = CMAPsize;
fwrite(&lng, 4, 1, ofil);
for (i = 0; i < 32; i++) {
str[0] = (colorstats[i][0] >> 4) & 0xF0;
str[1] = (colorstats[i][0]) & 0xF0;
str[2] = (colorstats[i][0] << 4) & 0xF0;
fwrite(str, 1, 3, ofil);
}
sprintf(str, "BODY");
fwrite(str, 1, 4, ofil);
pos2 = ftell(ofil);
lng = BODYsize;
fwrite(&lng, 4, 1, ofil);
lng = 0L;
for (i = 0; i < image->hdr.rows; i ++) {
for (j = 0; j < depth; j++) {
if (comp) {
dest = destbuf;
wrd = PackRow(&planes[j], &dest, row_size);
lng += (long) wrd;
fwrite(destbuf, 1, wrd, ofil);
}
else {
fwrite(planes[j], 1, row_size, ofil);
planes[j] += row_size;
}
}
}
/* make BODY a multiple of 4 bytes in length */
byt = 0;
if (comp)
while (lng % 4) {
fwrite(&byt, 1, 1, ofil); /* pad */
lng++;
}
if (comp) {
fseek(ofil, (long) pos2, 0);
fwrite(&lng, 4, 1, ofil);
lng -= BODYsize;
lng += FORMsize;
fseek(ofil, (long) pos1, 0);
fwrite(&lng, 4, 1, ofil);
}
return 1;
}
/* get the next encoding for a pixel, based on the previous pixel and the
* current 12 bit RGB value */
#define BPP 4 /* Bits per pixel */
#define MAXGRAY (1 << BPP)
#define ABS(x) ((x) < 0 ? -(x) : (x))
/*-
* first, check to see if pixel is the same color. if so, return
* check if only one of R, G, B have changed. if so, return new pixel.
* find closest entry in colormap. if exact match, return it.
* modify one of R, G, B (whichever difference is largest)
* compare previous calculation to best match in color table. return
* whichever is a better match.
-*/
getcolor(ppix, crgb, prgb)
int ppix, *crgb, prgb;
{
int i, j, val, cr, cg, cb, pr, pg, pb, nr, ng, nb, best, dist, nrgb;
/* if same color, then do a NOOP (same as previous pixel) */
if (*crgb == prgb)
return (ppix);
/* set up for comparisons */
cb = *crgb & (MAXGRAY - 1);
cg = (*crgb >> BPP) & (MAXGRAY - 1);
cr = (*crgb >> (BPP * 2)) & (MAXGRAY - 1);
pb = prgb & (MAXGRAY - 1);
pg = (prgb >> BPP) & (MAXGRAY - 1);
pr = (prgb >> (BPP * 2)) & (MAXGRAY - 1);
/* see if only one plane changed, if so, use HAM encoding */
if (prgb != -1) {
if (pr == cr && pg == cg)
return (cb + 0x10);
if (pr == cr && pb == cb)
return (cg + 0x30);
if (pg == cg && pb == cb)
return (cr + 0x20);
}
/* else look for an exact match in the color table (or minimal distance) */
for (i = 0; i < colors; i++) {
if (colorstats[i][0] == *crgb)
return (i);
if (i == 0) {
best = 0;
dist = distance(colorstats[i][0], *crgb);
}
else if ((j = distance(colorstats[i][0], *crgb)) < dist) {
best = i;
dist = j;
}
}
/* do a forced absolute */
if (prgb == -1) {
*crgb = colorstats[best][0];
return (best);
}
/* find which color is off the most from previous */
i = 0;
val = ABS(cr - pr);
if (ABS(cg - pg) > val) {
i = 1;
val = ABS(cg - pg);
}
if (ABS(cb - pb) > val) {
i = 2;
val = ABS(cb - pb);
}
nr = pr;
ng = pg;
nb = pb;
switch (i) {
case 0:
nr = cr;
val = nr + 0x20;
break;
case 1:
ng = cg;
val = ng + 0x30;
break;
case 2:
nb = cb;
val = nb + 0x10;
break;
}
nrgb = (nr << (2 * BPP)) + (ng << BPP) + nb;
/* now pick the best */
if (distance(nrgb, *crgb) < dist) {
/* do a best relative */
*crgb = nrgb;
return (val);
}
/* do a best absolute */
*crgb = colorstats[best][0];
return (best);
}
/* calculate distance between two colors in 3D space */
distance(argb, brgb)
{
int b, g, r;
/* set up for comparisons */
b = argb & (MAXGRAY - 1);
g = (argb >> BPP) & (MAXGRAY - 1);
r = (argb >> (BPP * 2)) & (MAXGRAY - 1);
b -= brgb & (MAXGRAY - 1);
g -= (brgb >> BPP) & (MAXGRAY - 1);
r -= (brgb >> (BPP * 2)) & (MAXGRAY - 1);
return (r * r + g * g + b * b);
}